#include <iostream>
#include <stdio.h>

class Base
{
	public:
		virtual void function_1()
		{
			printf("Base function_1...\n");
		}

		virtual void function_2()
		{
			printf("Base function_2...\n");
		}
	
		virtual void function_3()
		{
			printf("Base function_3...\n");
		}
};

class Sub : Base
{
	public:
		virtual void function_1()
		{
			printf("Sub function_1...\n");
		}

		virtual void function_2()
		{
			printf("Sub function_2...\n");
		}
	
		virtual void function_6()
		{
			printf("Sub function_6...\n");
		}
};

int main(int argc, char ** argv)
{
	Sub sub;
	Sub *pb;

	printf("sizeof = %ld\n",  sizeof(class Base));		/* 8 */
	printf("sizeof = %ld\n",  sizeof(class Sub));		/* 8 */

	printf("sub address is 0x%x.\n", (unsigned int)(long)&sub);		// this 指针，是堆栈里面的地址。虚表的地址不是this指针，是this指针的前8个字节。
	printf("sub virtual table address is:0x%x\n", (unsigned int)*(long*)&sub);   	// 虚表地址。取this指针的前8个字节。*(char*)&sub为取一个字节。	

	typedef void(*pfunction)(void);	
	pfunction pf;

	long temp = *((long*)(*(long*)&sub) + 0);		// 取地址表里面的第一个值
													// *(long*)&sub) 是虚表地址
													// (long*) 转换成一个int*类型。
													// *((long*)(*(long*)&sub) + 0)， 加的是0，即取第一个8字节值。

	pf = (pfunction)*((long*)(*(long*)&sub) + 0);	// 或这样，取地址表里面的第一个值
	pf();						// function_1

	pf = (pfunction)*((long*)(*(long*)&sub) + 1);	// 取地址表里面的第二个8字节值
	pf();						// function_2

	for (int i = 0; i < 4; i++) {
		pf = (pfunction)*((long*)(*(long*)&sub) + i);
		pf();
	}

	/*
		Sub function_1...
		Sub function_2...
		Base function_3...
		Sub function_6...
	*/
}

#if 0

1.类对象的首地址里面存的第一个8字节才是虚表的地址。即this指针里面存的第一个8字节就是虚表的地址。
2.虚表的地址不是 this 指针，是 this 指针的前8个字节。
3.虚表的个数是编译的时候确定的，有几个 virtual 存几个。

int main(int argc, char ** argv)
{
    11c9:	f3 0f 1e fa          	endbr64 
    11cd:	55                   	push   %rbp
    11ce:	48 89 e5             	mov    %rsp,%rbp
    11d1:	48 83 ec 40          	sub    $0x40,%rsp
    11d5:	89 7d cc             	mov    %edi,-0x34(%rbp)
    11d8:	48 89 75 c0          	mov    %rsi,-0x40(%rbp)
    11dc:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
    11e3:	00 00 
    11e5:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
    11e9:	31 c0                	xor    %eax,%eax
	Sub sub;
    11eb:	48 8d 05 46 2b 00 00 	lea    0x2b46(%rip),%rax        # 3d38 <_ZTV3Sub+0x10>
    11f2:	48 89 45 e0          	mov    %rax,-0x20(%rbp)
	Sub *pb;

	printf("sizeof = %ld\n",  sizeof(class Base));		/* 8 */
    11f6:	be 08 00 00 00       	mov    $0x8,%esi
    11fb:	48 8d 3d 50 0e 00 00 	lea    0xe50(%rip),%rdi        # 2052 <_ZStL19piecewise_construct+0x4a>
    1202:	b8 00 00 00 00       	mov    $0x0,%eax
    1207:	e8 84 fe ff ff       	callq  1090 <printf@plt>
	printf("sizeof = %ld\n",  sizeof(class Sub));		/* 8 */
    120c:	be 08 00 00 00       	mov    $0x8,%esi
    1211:	48 8d 3d 3a 0e 00 00 	lea    0xe3a(%rip),%rdi        # 2052 <_ZStL19piecewise_construct+0x4a>
    1218:	b8 00 00 00 00       	mov    $0x0,%eax
    121d:	e8 6e fe ff ff       	callq  1090 <printf@plt>

	printf("sub address is 0x%x.\n", (unsigned int)(long)&sub);		// this 指针，是堆栈里面的地址。虚表的地址不是this指针，是this指针的前8个字节。
    1222:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1226:	89 c6                	mov    %eax,%esi
    1228:	48 8d 3d 31 0e 00 00 	lea    0xe31(%rip),%rdi        # 2060 <_ZStL19piecewise_construct+0x58>
    122f:	b8 00 00 00 00       	mov    $0x0,%eax
    1234:	e8 57 fe ff ff       	callq  1090 <printf@plt>
	printf("sub virtual table address is:0x%x\n", (unsigned int)*(long*)&sub);   	// 虚表地址。取this指针的前8个字节。*(char*)&sub为取一个字节。	
    1239:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    123d:	48 8b 00             	mov    (%rax),%rax
    1240:	89 c6                	mov    %eax,%esi
    1242:	48 8d 3d 2f 0e 00 00 	lea    0xe2f(%rip),%rdi        # 2078 <_ZStL19piecewise_construct+0x70>
    1249:	b8 00 00 00 00       	mov    $0x0,%eax
    124e:	e8 3d fe ff ff       	callq  1090 <printf@plt>

	typedef void(*pfunction)(void);	
	pfunction pf;

	long temp = *((long*)(*(long*)&sub) + 0);		// 取地址表里面的第一个值
    1253:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1257:	48 8b 00             	mov    (%rax),%rax
    125a:	48 8b 00             	mov    (%rax),%rax
    125d:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
													// *(long*)&sub) 是虚表地址
													// (long*) 转换成一个int*类型。
													// *((long*)(*(long*)&sub) + 0)， 加的是0，即取第一个8字节值。

	pf = (pfunction)*((long*)(*(long*)&sub) + 0);	// 或这样，取地址表里面的第一个值
    1261:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1265:	48 8b 00             	mov    (%rax),%rax
    1268:	48 8b 00             	mov    (%rax),%rax
    126b:	48 89 45 f0          	mov    %rax,-0x10(%rbp)
	pf();						// function_1
    126f:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
    1273:	ff d0                	callq  *%rax

	pf = (pfunction)*((long*)(*(long*)&sub) + 1);	// 取地址表里面的第二个8字节值
    1275:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1279:	48 8b 00             	mov    (%rax),%rax
    127c:	48 83 c0 08          	add    $0x8,%rax
    1280:	48 8b 00             	mov    (%rax),%rax
    1283:	48 89 45 f0          	mov    %rax,-0x10(%rbp)
	pf();						// function_2
    1287:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
    128b:	ff d0                	callq  *%rax

	for (int i = 0; i < 4; i++) {
    128d:	c7 45 dc 00 00 00 00 	movl   $0x0,-0x24(%rbp)
    1294:	83 7d dc 03          	cmpl   $0x3,-0x24(%rbp)
    1298:	7f 2a                	jg     12c4 <main+0xfb>
		pf = (pfunction)*((long*)(*(long*)&sub) + i);
    129a:	8b 45 dc             	mov    -0x24(%rbp),%eax
    129d:	48 98                	cltq   
    129f:	48 8d 14 c5 00 00 00 	lea    0x0(,%rax,8),%rdx
    12a6:	00 
    12a7:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    12ab:	48 8b 00             	mov    (%rax),%rax
    12ae:	48 01 d0             	add    %rdx,%rax
    12b1:	48 8b 00             	mov    (%rax),%rax
    12b4:	48 89 45 f0          	mov    %rax,-0x10(%rbp)
		pf();
    12b8:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
    12bc:	ff d0                	callq  *%rax
	for (int i = 0; i < 4; i++) {
    12be:	83 45 dc 01          	addl   $0x1,-0x24(%rbp)
    12c2:	eb d0                	jmp    1294 <main+0xcb>
		Sub function_1...
		Sub function_2...
		Base function_3...
		Sub function_6...
	*/
}
    12c4:	b8 00 00 00 00       	mov    $0x0,%eax
    12c9:	48 8b 4d f8          	mov    -0x8(%rbp),%rcx
    12cd:	64 48 33 0c 25 28 00 	xor    %fs:0x28,%rcx
    12d4:	00 00 
    12d6:	74 05                	je     12dd <main+0x114>
    12d8:	e8 d3 fd ff ff       	callq  10b0 <__stack_chk_fail@plt>
    12dd:	c9                   	leaveq 
    12de:	c3                   	retq   


#endif
